/********************************************************************************

* @File name: ftm.h

* @Author: Pan Zhimin

* @Version: 1.0

* @Date: 2023-8-21

* @Description: FTM SA & FA TEST

******************************************************************************* */

#ifndef _FTM_H_
#define _FTM_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include "ql_log.h"
#include "ql_fs.h"
#include "ql_api_sim.h"
#include "ql_api_osi.h"
#include "ql_api_dev.h"

#include "ql_uart.h"
#include "ql_power.h"
#include "ql_api_nw.h"
#include "ql_wifi_scan.h"
#include "ql_http_client.h"
#include "ql_api_ble_gatt.h"
#include "ql_api_bt.h"
#include "ql_adc.h"
#include "ql_app_feature_config.h"

#include "zq_app.h"

#ifdef ZY_SIMULATOR
#include "simu_800.h"
#endif

#ifdef __cplusplus
extern "C"
{
#endif

    /* public define */
#define     ROUND10(n)                              ((n + (n >= 0 ? 5 : -5)) / 10 * 10)

#define     OK                                      (1)
#define     FAIL                                    (2)
#define     NOT_SUPPORT                             (3)

#define     MAX(a,b)                                ((a) > (b) ? (a) : (b))
#define     MIN(a,b)                                ((a) < (b) ? (a) : (b))

#define     FTM_TAG                                 "\n[FTM]"

#define     FTM_LOG(format, ...)                    QL_LOG_PRINTF_TAG(QL_LOG_LEVEL_INFO, QL_LOG_TAG_OPEN, FTM_TAG": %s(%d) - "format"", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define     FTM_LOG_ERR(format, ...)                QL_LOG_PRINTF_TAG(QL_LOG_LEVEL_ERROR, QL_LOG_TAG_OPEN, FTM_TAG": %s(%d) - "format"", __FUNCTION__, __LINE__, ##__VA_ARGS__)

#define     DELAYms(ms)                             ql_rtos_task_sleep_ms(ms)

// hex_buf超长就分段: {aa bb cc ... xx yy zz}
#define     FTM_HEX_BUF_LOG(head, hex_buf, hex_len) do {                                                                                              \
                                                          char content[200] = "{ ";                                                                   \
                                                          int offset = strlen(content);                                                               \
                                                          int LEN = 0;                                                                                \
                                                          char SEP[] = "...";                                                                         \
                                                          if (strlen(head) + 2/*"{ "*/ + hex_len * 3 + 2/*"} "*/ > sizeof(content)) {                 \
                                                              LEN = (sizeof(content) - strlen(head) - 2 - 2 - strlen(SEP)) / (3 * 2);                 \
                                                          }                                                                                           \
                                                          if (LEN != 0) {                                                                             \
                                                              for (int i = 0; i < LEN; i++) {                                                         \
                                                                  sprintf(content + offset, "%02X ", (hex_buf)[i]);                                   \
                                                                  offset += strlen("2X ");                                                            \
                                                              }                                                                                       \
                                                              strcat(content, SEP);                                                                   \
                                                              offset += strlen(SEP);                                                                  \
                                                              for (int i = hex_len - LEN; i < hex_len; i++) {                                         \
                                                                  sprintf(content + offset, "%02X ", (hex_buf)[i]);                                   \
                                                                  offset += strlen("2X ");                                                            \
                                                              }                                                                                       \
                                                          } else {                                                                                    \
                                                              for (int i = 0; i < hex_len; i++) {                                                     \
                                                                  sprintf(content + offset, "%02X ", (hex_buf)[i]);                                   \
                                                                  offset += strlen("2X ");                                                            \
                                                              }                                                                                       \
                                                          }                                                                                           \
                                                          strcat(content, "}");                                                                       \
                                                          FTM_LOG("%s[%d] = %s", head, hex_len, content);                                             \
                                                    } while (0)


    /* private define */

#define     CAT1_IS_MASTER                          (CFG_DEVICE_TYPE == 906 || CFG_DEVICE_TYPE == 24002 || CFG_DEVICE_TYPE == 24003)        // M0是主芯片, Cat1是通讯芯片
#define     CAT1_THREAD_RESIDENT                    (CFG_DEVICE_TYPE == 906 || CFG_DEVICE_TYPE == 24002 || CFG_DEVICE_TYPE == 24003)        // Cat1线程不常驻
#define     HAS_485_UART                            (CFG_DEVICE_TYPE == 866 || CFG_DEVICE_TYPE == 885 || CFG_DEVICE_TYPE == 891 || CFG_DEVICE_TYPE == 980)
#define     HAS_NTC_CAL                             (CFG_DEVICE_TYPE == 24002 || CFG_DEVICE_TYPE == 24003)                                  // NTC校准
#ifdef ZQ_BLE_PLATFORM
#define     HAS_UUID                                (ZQ_BLE_PLATFORM == 8910)
#else
#define     HAS_UUID                                (false)
#endif                                                           // Cat1芯片是否含有蓝牙
#define     HAS_SPI                                 (false)                                                                                 // 是否含有外部flash
#define     HAS_MP3                                 (false)                                                                                 // 是否有mp3播放功能
#define     FTM_SPI_FILENAME                        ("EFS:spi_test")
#define     FTM_MP3_FILENAME                        ("EFS:self_beep.mp3")


/******************************************************************************************************************
Note:
pin、func、gpio 请查看quec硬件设计 (Quectel_EC800G-CN_QuecOpen_GPIO_Configuration_V1.0.0_Preliminary_20220316.xlsx)
*******************************************************************************************************************/

// 1, Uart1: 跟M0通信 -> 原理图 MAIN_TX(TX: 18) MAIN_RX(RX: 17), 无需配置pin及func
#define     FTM_MCU_UART_PORT                       (QL_UART_PORT_1)
#if (CFG_DEVICE_TYPE == 24002 || CFG_DEVICE_TYPE == 24003)
#define     FTM_MCU_UART_BAUD                       (QL_UART_BAUD_1200)
#else 
#define     FTM_MCU_UART_BAUD                       (QL_UART_BAUD_230400)
#endif

// 2, Uart3: 跟PC通信 -> 原理图 AUX_TX(TX: 29) AUX_RX(RX: 28)  || 作为485通讯口 -> 原理图 485_CTL(20)
#if CAT1_IS_MASTER || HAS_485_UART
#define     FTM_PC_UART_PORT                        (QL_UART_PORT_3)
#define     FTM_PC_UART_BAUD                        (QL_UART_BAUD_115200)
#define     FTM_PC_UART_TX_PIN                      (29)                  // PIN号 (xx)
#define     FTM_PC_UART_RX_PIN                      (28)                  // PIN号 (yy)
#define     FTM_PC_UART_TX_FUNC                     (0x04)                // FTM_PC_UART_TX_PIN(xx) -> UART_3_TXD -> Alternate Function 4
#define     FTM_PC_UART_RX_FUNC                     (0x04)                // FTM_PC_UART_RX_PIN(yy) -> UART_3_RXD -> Alternate Function 4
#if HAS_485_UART
#define     FTM_485_CTL                             ((CFG_DEVICE_TYPE == 866 || CFG_DEVICE_TYPE == 885 || CFG_DEVICE_TYPE == 891) ? 0 : 20)  // PIN号 (用的是电平转换芯片, 如果是0则为MOS管转换)
#if FTM_485_CTL
#define     FTM_485_GPIO                            (GPIO_1)              // FTM_485_CTL(PIN号xx) -> FTM_485_GPIO(GPIO_xx)
#define     FTM_485_CTRL_FUNC                       (0x00)                // FTM_485_CTL(PIN号xx) -> FTM_485_GPIO(GPIO_xx) -> Alternate Function 0
#define     FTM_485_UART_BAUD                       (QL_UART_BAUD_460800) // 跟业务代码匹配
#else
#define     FTM_485_UART_BAUD                       (QL_UART_BAUD_115200) // 用的是MOS管没有电平转换芯片快速, 所以不支持高速
#endif
#endif
#endif


#if CAT1_IS_MASTER
#define     SEND_RESPONSE(cmd, buf, len)            ftm_pkt_send(FTM_PC_UART_PORT, cmd, buf, len)
#elif HAS_485_UART
#define     SEND_RESPONSE(cmd, buf, len)            (cmd == FTM_485_TEST? ftm_pkt_send(FTM_PC_UART_PORT, cmd, buf, len) : ftm_pkt_send(FTM_MCU_UART_PORT, cmd, buf, len))
#else
#define     SEND_RESPONSE(cmd, buf, len)            ftm_pkt_send(FTM_MCU_UART_PORT, cmd, buf, len)
#endif

#define     SEND_RESULT(cmd, value)                 do {                                                                                                        \
                                                        int result = (int)value;                                                                                \
                                                        uint8_t buf = (result < 0)? (NOT_SUPPORT) : (result > 0 ? OK : FAIL);                                   \
                                                        SEND_RESPONSE(cmd, &buf, sizeof(buf));                                                                  \
                                                        FTM_LOG("SEND_RESPONSE: (0x%02X, %s)", cmd, result < 0 ? "Not Support" : (result > 0 ? "Ok" : "Fail")); \
                                                    } while (0)


/* command define */
#define     FTM_SLAVE_CMD_PREFIX                    (0x8000)

#if CAT1_IS_MASTER
#define     FTM_M0_INFO                             (0x8103)            // PC直接发送给Cat1的, 要获取m0的信息
#endif

#define     CAT1_QUERY_M0_INFO                      (0x0001)            // Cat1发给M0的业务包(用于查询sw或bootMode)
#define     FTM_485_TEST                            (0x8143)            // PC发送给Cat1的, 检查485通路

#define     FTM_MASTER_CMD_PREFIX                   (0x9000)
#define     FTM_SN                                  (0x9100)
#define     FTM_HW                                  (0x9101)
#define     FTM_SW                                  (0x9102)
#define     FTM_BT_UUID                             (0x9105)
#define     FTM_BOOTMODE                            (0x9106)
#define     FTM_SIM_ICCID                           (0x9107)
#define     FTM_SIM_NUM                             (0x9108)
#define     FTM_SIM_IMSI                            (0x9109)
#define     FTM_IMEI                                (0x910A)
#define     FTM_WIFI_BT_MAC                         (0x910B)
#define     FTM_OTA_URL_CONFIG                      (0x910D)
#define     FTM_WRITE_MP3                           (0x9110)
#define     FTM_GPRS_STATUS                         (0x9131)
#define     FTM_WIFI_STATUS                         (0x9132)
#define     FTM_SPI_TEST                            (0x9134)
#define     FTM_NTC_CAL                             (0x9135)
#define     FTM_PLAY_MP3                            (0x9136)
#define     FTM_M0_REPORT_FA_RESULT                 (0x9300)


/* packet 头固定格式 */
#define     PKT_PREFIX                              { 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
#define     PKT_SUFFIX                              (0xBB)
#define     PKT_HEAD_SZ                             (8/*sizeof(PKT_PREFIX)*/ + 2 /* cmd(0xaabb) */ + 1 /* data_len(0xcc) */)

#define     RSP_NOT_SET                             (0xFF)

#define     RECV_BUF_SIZE                           (1*1024)

#define     QL_UART_EVENT_CNT      		            (5)
#define     QL_UART_RX_BUFFER_SIZE                  (2048)
#define     QL_UART_STACK_SIZE     		            (4096)

#define     SIM_INDEX						        (0)
#define     ICCID_LEN                               (25)
#define     IMSI_LEN                                (20)
#define     IEMI_LEN                                (20)

#define     FTM_SRV_URL                             "http://iot.origin-tech.cn:8888/verify/addTestResult"

#define     DEFAULT_SN_DEVICE_ID_VAL                "NA1234567890"
#define     DEVINFO_NV_FLAG                         0x11223345

#define     BLE_UUID_DEFAULT                        { 0xAB, 0x81, 0x90, 0xD5, 0xD1, 0x1E, 0x49, 0x41, 0xAC, 0xC4, 0x42, 0xF3, 0x05, 0x10, 0xB4, 0x08 }
#define     BLE_MAJOR_DEFAULT                       (0xFFFF)
#define     BLE_MINOR_DEFAULT                       (0xFFFF)

#define     BASE_RSSI_MIN                           (-128)

#ifndef     BASE_BUFLEN_MAX
#define     BASE_BUFLEN_MAX                         (250)
#endif

#ifndef     RES_GOOD
#define     RES_GOOD			                    (0)
#endif

#ifndef     RES_FAIL
#define     RES_FAIL			                    (-1)
#endif

#ifndef     QL_HTTP_OK
#define     QL_HTTP_OK                              (0)
#endif


    /* Cat1 mode */
    enum {
        MODE_LP = 0,
        MODE_SA,
        MODE_FA,
        MODE_WK,
    };

    /* packet command type */
    enum {
        CMD_NONE = 0,
        CMD_GET,                                    /* read */
        CMD_SET,                                    /* write */
    };

    /* FA 测试结果 */
    enum {
        FA_TESTING = 0,                             /* 测试中 */
        FA_TEST_OK,                                 /* 测试完成 */
        FA_UPLOAD_OK,                               /* 上传服务器完成 */
    };

    /* packet struction */
    typedef struct {
        uint16_t cmd;                               /* 命令ID */
        uint8_t* data;                              /* 数据内容 */
        uint32_t data_len;                          /* 数据长度 */
        uint8_t sum;                                /* check sum */
    } pkt_t;

    typedef struct {
        uint8_t port;                               /* 端口号 */
        ql_uart_baud_e baudrate;                    /* 波特率 */
    } uart_config;


    typedef enum {
        FOTA_HTTP_DOWN_INIT,		                //初始阶段
        FOTA_HTTP_DOWN_DOWNING,		                //下载中
        FOTA_HTTP_DOWN_INTR,		                //下载被中断
        FOTA_HTTP_DOWN_DOWNED,		                //下载完成
        FOTA_HTTP_DOWN_NOSPACE,		                //没有空间
    } e_fota_down_stage;

    typedef struct {
        bool is_show;			                    //是否显示进度
        uint32_t total_size;		                //文件总共大小
        uint32_t dload_size;		                //已经下载大小
        int file_size;			                    //上次升级中断保存的文件大小
    } http_progress_t;

    typedef struct {
        http_client_t http_cli;		                //和http交互创建的struct http_client_s类型的指针
        bool b_is_http_range;		                //是否发送http get range 报文
        int profile_idx;			                //cid
        uint8_t sim_id;				                //simid
        http_progress_t http_progress;		        //http进度
        e_fota_down_stage e_stage;				    //http下载固件包的阶段
        QFILE			fd;						    //写文件的文件描述符
        int		i_save_size;					    //在一次下载的过程中，分为多次写入，保存的是上次的写入大小，用于控制满多少字节写一次
        uint32_t   last_precent;				    //下载最后一次百分比
        bool	b_is_have_space;				    //存储空间是否可用
        int    chunk_encode;                        //Transfer-Encoding:chunked 传输方式
    } http_client;


    typedef struct {
        uint8_t  ble_uuid[16];
        uint16_t ble_major;
        uint16_t ble_minor;

        uint8_t  sim_num[32];
        uint8_t  sim_len;
        /* Note: 把以上5项放到头部, 是为了别处引用时可以不管下面的N项 */

        int32_t  bootmode; // 01:SA, 02:FA, 03:WK

        uint8_t  sn[32];
        uint8_t  sn_len;

        uint8_t  did[32];
        uint8_t  did_len;

        uint32_t flag;
        // Note: 从此往后添加新的item

#if HAS_OTA_URL
        // note: 新加参数要放到最后, flag的后面 ！
        /* OTA服务器地址和端口号 */
        uint8_t  ota_url[128];
        uint8_t  ota_url_len;
        uint16_t ota_port;
#endif

#if HAS_NTC_CAL
        int      adc_value;
#endif
    } devinfo_t;

#if HAS_485_UART
    typedef struct {
        bool exist;                                 // 是否存在这一路485
        bool result;                                // 这一路485的测试结果
    } uart_485_t;
#endif

    /* export functions */
    extern bool ftm_start(void);
    extern bool ftm_bootmode_write(uint8_t bootmode, bool reboot);

#ifdef __cplusplus
}
#endif

#endif  // _FTM_H_
